/*
 * 教材中的示例程序之完整版.
 * 书名：《编译原理基础》
 * 版次：第三版
 * 作者：王献青，张立勇，张淑平，刘坚
 * 出版社：西安电子科技大学出版社
 * 程序完成时间：2025.03
 */

//
// 文件内容：简易的词法分析器定义，
//
// 函数 nextToken() 可被看做是词法分析其的关键API操作，
//    识别一个记号，但仅返回记号类别，没有实现记号的存储。
//    按条件调用不同的记号识别函数。
// [提示] 教材2.5.2节讨论了如何存储记号，可研读AMCC中的实现。
//

#include <string.h>

#include "lexer.h"

// 简易的输入缓冲区(见教材2.5.1节)，本示例不考虑其更新
char buffer[BUFFER_SIZE+1];
char * lexeme_begin;    // 指向单词的第一个字符
char * forward;  // 指向当前字符，用于向前扫描
                 // 当识别到一个单词后，它指向该单词的最后一个字符

//教材 2.5 节给出的三种识别函数
extern TokenKind recognize_word_direct_coded_1();
extern TokenKind recognize_word_direct_coded_2();
extern TokenKind recognize_word_hand_coded();

static void skip_whitespaces();

static int lexer_style = 3;

/*
 * 设置本次程序执行所用的识别函数.
 * 参数 style 取值：
 *   1: 直接编码的_1: 基于状态变量变化的实现
 *   2: 直接编码的_2: 基于 goto 的实现
 *   3或其他值: 手工编码的:   递归下降风格的实现
 */
void  set_lexer_style(int style) {
    switch(style){
        case 1: case 2:
            lexer_style = style;    break;
        default:
            lexer_style = 3;        break;
    }
}

//设置待分析的输入，将其复制到内部缓冲区
void set_lexer_input(const char * input) {
    strncpy(buffer, input, BUFFER_SIZE);
    buffer[BUFFER_SIZE] = '\0';
    lexeme_begin = buffer;
    forward = buffer - 1; // 在 nextToken() 中要递增的
}

//获取当前单词的起始位置
const char * get_word_begin(){
    return lexeme_begin;
}

//获取当前单词的长度
int  get_word_length(){
    return (int)(forward-lexeme_begin+1);
}

/*
 * 简易的记号识别函数：按调用者意图调用对应的实现版本.
 *
 * 返回记号类别，所得记号的文本（单词）由两个指针指示：
 *   单词是在范围 [lexeme_begin, forward] 内的字符序列。
 * 若返回ERROR(发现一个错误)，则forward恰好指向这次发现的无效字符。
 */
TokenKind  nextToken() {

    ++forward;  // 指向下一单词开始，或空白字符，或 EOF
                // 递增理由：此前，该指针指向了上一单词的最后一个字符,
                //     或 指向了一个无效字符

    //  因为图2.26给出的DFA并未考虑空白字符，所以必须在
    //  这里处理
    skip_whitespaces();      // 跳过单词前面可能存在的空白字符

    lexeme_begin = forward;  // 两个指针：指向下一单词的第一个字符

    switch(lexer_style) {  // 识别下一单词
        case 1:  // 直接编码的_1: 基于状态变量变化的实现
            return recognize_word_direct_coded_1();
        case 2:  // 直接编码的_2: 基于 goto 的实现
            return recognize_word_direct_coded_2();
        case 3:  // 手工编码的: 递归下降风格的实现
        default:
            return recognize_word_hand_coded();
    }

    // 考虑到这个程序仅演示词法分析的核心--如何识别记号，
    // 所以这里不考虑记号的存储.
    // 教材2.5.2节讨论了如何存储记号，可研读AMCC中的实现。
}


// 跳过空白字符，此处仅考虑三个空白字符
static void skip_whitespaces() {
    char c = *forward;
    while (c==' ' || c=='\t' || c == '\n') {
        c = *(++forward);
    }
}

// 将记号种类编码翻译为文字，便于阅读程序输出.
//  ** 这是一个低效实现。当记号种类较多时，
//     最好使用哈希表或其他更高效的数据结构和查找算法。
const char * lookup_kindname(TokenKind kind) {
    switch(kind) {
        case EOFS:  return  "EOF";
        case ERROR: return  "ERROR";
        case ID:    return  "ID";
        case INT:   return  "INT";
        case REAL:  return  "REAL";
        case LT:    return  "LT";
        case LE:    return  "LE";
        case GT:    return  "GT";
        case GE:    return  "GE";
        case NE:    return  "NE";
        case EQ:    return  "EQ";
        case NOT:   return  "NOT";
        case ASSIGN: return "ASSIGN";
        default:    return "UNKNOWN";
    }
}
